package progetto;

import example.hot.InetCoordinates;
import peersim.cdsim.CDProtocol;
import peersim.core.CommonState;
import peersim.core.Node;
import peersim.config.Configuration;
import peersim.vector.SingleValueHolder;

public class RandWaypointMobility implements CDProtocol{

	
	// ------------------------------------------------------------------------
    // Constants
    // ------------------------------------------------------------------------
	/**
     * The coordinate protocol to look at.
     * 
     * @config
     */
    private static final String PAR_COORD_PROT = "coord_protocol";
    
    private static final String PAR_SPEED_PROT = "speed_protocol";
    
    private static final String PAR_STATUS_PROT = "status_protocol";
    
    private static final int MIN_STOP = 1;
    
    private static final int MAX_STOP = 10;
    
    private static final int STOP = 0;
    private static final int STARTED = 1;
	private static final int MOVED = 2;
	private static final int ARRIVED = 3;
	
	
    // ------------------------------------------------------------------------
    // Fields
    // ------------------------------------------------------------------------

	private static int statusPid;
	
    /**
     * {@link #PAR_COORD_PROT}
     */
    private static int coordPid;
    
    private static int speedPid;
    
    private int stopRuns;
    
    //private double distanceInARun;
    private double distanceInARunX;
    private double distanceInARunY;
    
    private int status;
    
    
    
    
    // ------------------------------------------------------------------------
    // Constructor
    // ------------------------------------------------------------------------
	public RandWaypointMobility(String prefix) {
		coordPid = Configuration.getPid(prefix + "." + PAR_COORD_PROT );
		speedPid = Configuration.getPid(prefix + "." + PAR_SPEED_PROT );
		statusPid = Configuration.getPid(prefix + "." + PAR_STATUS_PROT);
		stopRuns = 0;
		status = STOP;
		
		
	}
	
	@Override
	public void nextCycle(Node node, int protocolID) {

		NodeStatus ns = (NodeStatus)node.getProtocol(statusPid);
		//if the node isn't fixed, 
		if(!ns.isFixed()) {
			//execute the random waypoint mobility protocol
			executeAction(node);
		} //else System.out.println("sono fermo!!!");
	}

	private void executeAction(Node node) {
		DynamicCoordinates coordProt = (DynamicCoordinates)node.getProtocol(coordPid);
		MobilitySpeed mSpeed = (MobilitySpeed)node.getProtocol(speedPid);
		switch(status) {

		case STOP: 
			//System.out.println("sto ferma ancora per "+ stopRuns);
			//controllo se ho finito di stare fermo
			if(stopRuns != 0) {
				//posso muovermi

				//diminuisco il numero di runs di stop
				stopRuns--;
				break;

			} else {
				status = STARTED;
			}


		case STARTED:
			//System.out.println("parto da "+ coordProt.getX() + coordProt.getY());
			
			//generate future coordinates
			coordProt.setFutureX(CommonState.r.nextDouble());
			coordProt.setFutureY(CommonState.r.nextDouble());
			
			//System.out.println("voglio arrivare in "+ coordProt.getFutureX() +" "+ coordProt.getFutureY());
			double speedRandom = mSpeed.generateRandomSpeed();
			//System.out.println("con velocità "+speedRandom);
			mSpeed.setCurrentSpeed(speedRandom);
	
			double distanceInARun = mSpeed.getCurrentSpeed();
			
			double distance = getEuclideanDistance(coordProt);
			double nRuns = distance/distanceInARun;
			//System.out.println("distanza di "+distance + " in "+nRuns + " runs, ogni run una distanza di "+ distanceInARun);
			double distanceX = EuclideanDistance.getDistanceMonoDimensional(coordProt.getX(), coordProt.getFutureX());
			double distanceY = EuclideanDistance.getDistanceMonoDimensional(coordProt.getY(), coordProt.getFutureY());
			
			distanceInARunX = distanceX/nRuns;
			distanceInARunY = distanceY/nRuns;

			//System.out.println("quindi ogni run in x mi muovo di "+ distanceInARunX + " in y di " +distanceInARunY);
			status = MOVED;
			//senza break perchè scelgo le nuove coordinate e mi muovo già

		case MOVED:
			
			//distance to the two dimensions in this moment
			double newDistanceX = EuclideanDistance.getDistanceMonoDimensional(coordProt.getX(), coordProt.getFutureX());
			double newDistanceY = EuclideanDistance.getDistanceMonoDimensional(coordProt.getY(), coordProt.getFutureY());
			
			//double valueToAdd = distanceInARunX;
			//se la distanza dei due punti ora è minore di quella che dovrei aggiungere allora sono arrivata
			if(newDistanceX <= distanceInARunX && newDistanceY <= distanceInARunY) {
				distanceInARunX = newDistanceX;
				distanceInARunY = newDistanceY;
				this.status = ARRIVED;
				
			} 
			//System.out.println("mi muovo! "+newDistanceX + " " +newDistanceY);
			moveIn(coordProt, distanceInARunX, distanceInARunY);
			//moveIn(coordProt, coordProt.getY(), coordProt.getFutureY(), );
			if(status!=ARRIVED)
				break;
				
		case ARRIVED:
			
			//decido quanti run il nodo dovrà stare fermo
			this.stopRuns =(int)( MIN_STOP + (MAX_STOP - MIN_STOP)*Math.random());
			status = STOP;
			//reset future coordinates
			coordProt.resetFutureCoord();
			//reset the distances to add at x and y each run
			resetDistance();
			//System.out.println("sono arrivato, le mie nuove cordinate sono "+ coordProt.getX() + " " + coordProt.getY());
			
		}

	}
	
	
	private void resetDistance() {
		
		this.distanceInARunY = -1;
		this.distanceInARunX = -1;
		
	}
	
	/*private boolean amIArrived(DynamicCoordinates coordProt) {
		
		return (coordProt.getX() == coordProt.getY()) && (coordProt.getFutureX() == coordProt.getFutureY());
	}*/
	
		
	private void moveIn(DynamicCoordinates coordProt, double valueX, double valueY) {
		coordProt.updateCurrentCoord(valueX, valueY);
	}
	
	
	
	private double getEuclideanDistance(DynamicCoordinates coordProt) {
		
		double x1 = coordProt.getX();
		double y1 = coordProt.getY();
		double x2 = coordProt.getFutureX();
		double y2 = coordProt.getFutureY();
		
		return EuclideanDistance.getDistance(x1, x2, y1, y2);
	}
	
	
	
	
	
	/**
	 * Clone.
	 */
	public Object clone()
	{
		RandWaypointMobility rwm = null;
		try { 
			rwm = (RandWaypointMobility)super.clone(); }
		catch( CloneNotSupportedException e ) {} // never happens
		return rwm;
	}

}
